package de.java.swing.extension.ui;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.LayoutManager;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Window;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;

import javax.swing.JFrame;
import javax.swing.WindowConstants;

import de.java.swing.extension.controller.ComponentMovementController;
import de.java.swing.extension.controller.ComponentResizeController;
import de.java.swing.extension.utils.ExtendedWindowConstants;
import de.java.swing.extension.utils.WindowStateConstants;

/**
 * TODO Kommentar.
 * 
 * @author Patrick Haas
 * @since 1.0.0
 * 
 */
public class ExtendedWindow implements WindowConstants, WindowStateConstants, ExtendedWindowConstants {

	/*
	 * Graphical
	 */

	JFrame window;

	private ExtendedPanel decorationPane;

	private ExtendedPanel northDecorationPane;

	private ExtendedPanel northCenterDecorationPane;

	private ExtendedPanel northWestDecorationPane;

	private ExtendedPanel northEastDecorationPane;

	private ExtendedPanel southDecorationPane;

	private ExtendedPanel southCenterDecorationPane;

	private ExtendedPanel southWestDecorationPane;

	private ExtendedPanel southEastDecorationPane;

	private ExtendedPanel westDecorationPane;

	private ExtendedPanel westCenterDecorationPane;

	private ExtendedPanel westNorthDecorationPane;

	private ExtendedPanel westSouthDecorationPane;

	private ExtendedPanel eastDecorationPane;

	private ExtendedPanel eastCenterDecorationPane;

	private ExtendedPanel eastNorthDecorationPane;

	private ExtendedPanel eastSouthDecorationPane;

	private Color decorationColor = DEFAULT_DECORATION_COLOR;

	private int decorationThickness = DEFAULT_DECORATION_THICKNESS;

	private int decorationCornerThicknessMultiplicator = DEFAULT_CORNER_DECORATION_THICKNESS_MULTIPLICATOR;

	protected ExtendedPanel rootPane;

	protected ExtendedPanel contentPane;

	protected ComponentResizeController componentResizeController;

	protected ComponentMovementController componentMovementController;

	protected PropertyChangeSupport stateChangeSupport = new PropertyChangeSupport(this);

	private boolean resizable;

	private boolean dragable;

	private boolean undecorated;

	/**
	 * Konstruktor.
	 * 
	 */
	public ExtendedWindow() {
		this("Untitled");
	}

	/**
	 * Konstruktor.
	 * 
	 * @param title
	 *            {@link String}
	 */
	public ExtendedWindow(String title) {
		window = new JFrame(title);
		initialize();
	}

	public Component add(Component comp) {
		return contentPane.add(comp);
	}

	public void remove(Component comp) {
		contentPane.remove(comp);
	}

	public Component add(Component comp, int index) {
		return contentPane.add(comp, index);
	}

	public void remove(int index) {
		contentPane.remove(index);
	}

	public Component add(String name, Component comp) {
		return contentPane.add(name, comp);
	}

	public void dispose() {
		window.dispose();
	}

	/**
	 * @param propertyName
	 * @param listener
	 * @see java.beans.PropertyChangeSupport#addPropertyChangeListener(java.lang.String,
	 *      java.beans.PropertyChangeListener)
	 */
	public void addPropertyChangeListener(String propertyName, PropertyChangeListener listener) {
		stateChangeSupport.addPropertyChangeListener(propertyName, listener);
	}

	/**
	 * @param propertyName
	 * @param listener
	 * @see java.beans.PropertyChangeSupport#removePropertyChangeListener(java.lang.String,
	 *      java.beans.PropertyChangeListener)
	 */
	public void removePropertyChangeListener(String propertyName, PropertyChangeListener listener) {
		stateChangeSupport.removePropertyChangeListener(propertyName, listener);
	}

	public Window getComponent() {
		return window;
	}

	public ExtendedPanel getContentPane() {
		return contentPane;
	}

	public Color getBackground() {
		return contentPane.getBackground();
	}

	public void setBackground(Color bg) {
		contentPane.setBackground(bg);
	}

	public Point getLocation() {
		return window.getLocation();
	}

	public void setLocation(int x, int y) {
		setLocation(new Point(x, y));
	}

	public void setLocation(Point p) {
		window.setLocation(p);
	}

	public int getState() {
		return window.getExtendedState();
	}

	public synchronized void setState(int state) {
		window.setExtendedState(state);
	}

	public boolean isResizable() {
		return resizable;
	}

	public void setResizable(boolean resizable) {
		window.setResizable(resizable);
		if (resizable)
			componentResizeController.registerComponent(window);
		else
			componentResizeController.deregisterComponent(window);
	}

	public int getDefaultCloseOperation() {
		return window.getDefaultCloseOperation();
	}

	public void setDefaultCloseOperation(int operation) {
		window.setDefaultCloseOperation(operation);
	}

	public Rectangle getMaximizedBounds() {
		return window.getMaximizedBounds();
	}

	public synchronized void setMaximizedBounds(Rectangle bounds) {
		window.setMaximizedBounds(bounds);
	}

	public Dimension getMinimumSize() {
		return window.getMinimumSize();
	}

	public void setMinimumSize(int width, int height) {
		setMinimumSize(new Dimension(width, height));
	}

	public void setMinimumSize(Dimension d) {
		window.setMinimumSize(d);
	}

	public boolean isDragable() {
		return dragable;
	}

	public void setDragable(boolean dragable) {
		componentMovementController.setActive(dragable);
	}

	public boolean isUndecorated() {
		return this.undecorated;
	}

	public synchronized void setUndecorated(boolean undecorated) {
		this.undecorated = undecorated;
		northDecorationPane.setVisible(!undecorated);
		southDecorationPane.setVisible(!undecorated);
		westDecorationPane.setVisible(!undecorated);
		eastDecorationPane.setVisible(!undecorated);
	}

	public boolean isVisible() {
		return window.isVisible();
	}

	/**
	 * Beeinflusst die Sichtbarkeit des Fensters.
	 * 
	 * @param visible
	 *            <code>true</code> Wenn das Fenster angezeigt werden soll
	 */
	public void setVisible(boolean visible) {
		window.setVisible(visible);
	}

	public Dimension getSize() {
		return window.getSize();
	}

	public void setSize(Dimension d) {
		window.setSize(d);
	}

	public void setSize(int width, int height) {
		this.setSize(new Dimension(width, height));
	}

	public LayoutManager getLayout() {
		return contentPane.getLayout();
	}

	public void setLayout(LayoutManager mgr) {
		contentPane.setLayout(mgr);
	}

	public void setDecorationColor(Color c) {
		decorationColor = c;

		northCenterDecorationPane.setBackground(decorationColor);
		northWestDecorationPane.setBackground(decorationColor);
		northEastDecorationPane.setBackground(decorationColor);

		southCenterDecorationPane.setBackground(decorationColor);
		southWestDecorationPane.setBackground(decorationColor);
		southEastDecorationPane.setBackground(decorationColor);

		westCenterDecorationPane.setBackground(decorationColor);
		westNorthDecorationPane.setBackground(decorationColor);
		westSouthDecorationPane.setBackground(decorationColor);

		eastCenterDecorationPane.setBackground(decorationColor);
		eastNorthDecorationPane.setBackground(decorationColor);
		eastSouthDecorationPane.setBackground(decorationColor);
	}

	public Color getDecorationColor() {
		return decorationColor;
	}

	public void setDecorationCornerThicknessMultiplicator(int multiplicator) {
		decorationCornerThicknessMultiplicator = multiplicator;
		updateDecorationThickness();
	}

	public int getDecorationCornerThicknessMulriplicator() {
		return decorationCornerThicknessMultiplicator;
	}

	public void setDecorationThickness(int thickness) {
		decorationThickness = thickness;
		updateDecorationThickness();
	}

	public int getDecorationThickness() {
		return decorationThickness;
	}

	private void initialize() {
		// Grundeinstellungen
		window.setSize(DEFAULT_SIZE);
		window.setLocation(DEFAULT_LOCATION);
		window.setLayout(new BorderLayout());
		window.setDefaultCloseOperation(EXIT_ON_CLOSE);

		// Entfernen des Standardrahmens
		window.setUndecorated(true);

		componentResizeController = new ComponentResizeController(window);
		componentMovementController = new ComponentMovementController(window);

		setDecoration();
		setRootPane();
		setContentPane();
	}

	private void updateDecorationThickness() {
		northCenterDecorationPane.setPreferredSize(new Dimension(decorationThickness * decorationCornerThicknessMultiplicator, decorationThickness));
		northWestDecorationPane.setPreferredSize(new Dimension(decorationThickness * decorationCornerThicknessMultiplicator, decorationThickness));
		northEastDecorationPane.setPreferredSize(new Dimension(decorationThickness * decorationCornerThicknessMultiplicator, decorationThickness));

		southCenterDecorationPane.setPreferredSize(new Dimension(decorationThickness * decorationCornerThicknessMultiplicator, decorationThickness));
		southWestDecorationPane.setPreferredSize(new Dimension(decorationThickness * decorationCornerThicknessMultiplicator, decorationThickness));
		southEastDecorationPane.setPreferredSize(new Dimension(decorationThickness * decorationCornerThicknessMultiplicator, decorationThickness));

		westCenterDecorationPane.setPreferredSize(new Dimension(decorationThickness, 0));
		westNorthDecorationPane.setPreferredSize(new Dimension(decorationThickness, decorationThickness * decorationCornerThicknessMultiplicator - decorationThickness));
		westSouthDecorationPane.setPreferredSize(new Dimension(decorationThickness, decorationThickness * decorationCornerThicknessMultiplicator - decorationThickness));

		eastCenterDecorationPane.setPreferredSize(new Dimension(decorationThickness, 0));
		eastNorthDecorationPane.setPreferredSize(new Dimension(decorationThickness, decorationThickness * decorationCornerThicknessMultiplicator - decorationThickness));
		eastSouthDecorationPane.setPreferredSize(new Dimension(decorationThickness, decorationThickness * decorationCornerThicknessMultiplicator - decorationThickness));
	}

	private void setDecoration() {
		decorationPane = new ExtendedPanel();
		decorationPane.setLayout(new BorderLayout());
		window.getContentPane().add(BorderLayout.CENTER, decorationPane);

		setNorthDecorationPanes();
		setSouthDecorationPanes();
		setWestDecorationPanes();
		setEastDecorationPanes();
	}

	private void setNorthDecorationPanes() {
		northDecorationPane = new ExtendedPanel();
		northDecorationPane.setLayout(new BorderLayout());
		decorationPane.add(BorderLayout.NORTH, northDecorationPane);

		northCenterDecorationPane = new ExtendedPanel(1.0f);
		northCenterDecorationPane.setCursor(new Cursor(Cursor.N_RESIZE_CURSOR));
		northCenterDecorationPane.setBackground(decorationColor);
		northCenterDecorationPane.setPreferredSize(new Dimension(decorationThickness * decorationCornerThicknessMultiplicator, decorationThickness));
		northDecorationPane.add(BorderLayout.CENTER, northCenterDecorationPane);

		northWestDecorationPane = new ExtendedPanel(1.0f);
		northWestDecorationPane.setCursor(new Cursor(Cursor.NW_RESIZE_CURSOR));
		northWestDecorationPane.setBackground(decorationColor);
		northWestDecorationPane.setPreferredSize(new Dimension(decorationThickness * decorationCornerThicknessMultiplicator, decorationThickness));
		northDecorationPane.add(BorderLayout.WEST, northWestDecorationPane);

		northEastDecorationPane = new ExtendedPanel(1.0f);
		northEastDecorationPane.setCursor(new Cursor(Cursor.NE_RESIZE_CURSOR));
		northEastDecorationPane.setBackground(decorationColor);
		northEastDecorationPane.setPreferredSize(new Dimension(decorationThickness * decorationCornerThicknessMultiplicator, decorationThickness));
		northDecorationPane.add(BorderLayout.EAST, northEastDecorationPane);
	}

	private void setSouthDecorationPanes() {
		southDecorationPane = new ExtendedPanel();
		southDecorationPane.setLayout(new BorderLayout());
		decorationPane.add(BorderLayout.SOUTH, southDecorationPane);

		southCenterDecorationPane = new ExtendedPanel(1.0f);
		southCenterDecorationPane.setCursor(new Cursor(Cursor.S_RESIZE_CURSOR));
		southCenterDecorationPane.setBackground(decorationColor);
		southCenterDecorationPane.setPreferredSize(new Dimension(decorationThickness * decorationCornerThicknessMultiplicator, decorationThickness));
		southDecorationPane.add(BorderLayout.CENTER, southCenterDecorationPane);

		southWestDecorationPane = new ExtendedPanel(1.0f);
		southWestDecorationPane.setCursor(new Cursor(Cursor.SW_RESIZE_CURSOR));
		southWestDecorationPane.setBackground(decorationColor);
		southWestDecorationPane.setPreferredSize(new Dimension(decorationThickness * decorationCornerThicknessMultiplicator, decorationThickness));
		southDecorationPane.add(BorderLayout.WEST, southWestDecorationPane);

		southEastDecorationPane = new ExtendedPanel(1.0f);
		southEastDecorationPane.setCursor(new Cursor(Cursor.SE_RESIZE_CURSOR));
		southEastDecorationPane.setBackground(decorationColor);
		southEastDecorationPane.setPreferredSize(new Dimension(decorationThickness * decorationCornerThicknessMultiplicator, decorationThickness));
		southDecorationPane.add(BorderLayout.EAST, southEastDecorationPane);
	}

	private void setWestDecorationPanes() {
		westDecorationPane = new ExtendedPanel();
		westDecorationPane.setLayout(new BorderLayout());
		decorationPane.add(BorderLayout.WEST, westDecorationPane);

		westCenterDecorationPane = new ExtendedPanel(1.0f);
		westCenterDecorationPane.setCursor(new Cursor(Cursor.W_RESIZE_CURSOR));
		westCenterDecorationPane.setBackground(decorationColor);
		westCenterDecorationPane.setPreferredSize(new Dimension(decorationThickness, 0));
		westDecorationPane.add(BorderLayout.CENTER, westCenterDecorationPane);

		westNorthDecorationPane = new ExtendedPanel(1.0f);
		westNorthDecorationPane.setCursor(new Cursor(Cursor.NW_RESIZE_CURSOR));
		westNorthDecorationPane.setBackground(decorationColor);
		westNorthDecorationPane.setPreferredSize(new Dimension(decorationThickness, decorationThickness * decorationCornerThicknessMultiplicator - decorationThickness));
		westDecorationPane.add(BorderLayout.NORTH, westNorthDecorationPane);

		westSouthDecorationPane = new ExtendedPanel(1.0f);
		westSouthDecorationPane.setCursor(new Cursor(Cursor.SW_RESIZE_CURSOR));
		westSouthDecorationPane.setBackground(decorationColor);
		westSouthDecorationPane.setPreferredSize(new Dimension(decorationThickness, decorationThickness * decorationCornerThicknessMultiplicator - decorationThickness));
		westDecorationPane.add(BorderLayout.SOUTH, westSouthDecorationPane);
	}

	private void setEastDecorationPanes() {
		eastDecorationPane = new ExtendedPanel();
		eastDecorationPane.setLayout(new BorderLayout());
		decorationPane.add(BorderLayout.EAST, eastDecorationPane);

		eastCenterDecorationPane = new ExtendedPanel(1.0f);
		eastCenterDecorationPane.setCursor(new Cursor(Cursor.W_RESIZE_CURSOR));
		eastCenterDecorationPane.setBackground(decorationColor);
		eastCenterDecorationPane.setPreferredSize(new Dimension(decorationThickness, 0));
		eastDecorationPane.add(BorderLayout.CENTER, eastCenterDecorationPane);

		eastNorthDecorationPane = new ExtendedPanel(1.0f);
		eastNorthDecorationPane.setCursor(new Cursor(Cursor.NE_RESIZE_CURSOR));
		eastNorthDecorationPane.setBackground(decorationColor);
		eastNorthDecorationPane.setPreferredSize(new Dimension(decorationThickness, decorationThickness * decorationCornerThicknessMultiplicator - decorationThickness));
		eastDecorationPane.add(BorderLayout.NORTH, eastNorthDecorationPane);

		eastSouthDecorationPane = new ExtendedPanel(1.0f);
		eastSouthDecorationPane.setCursor(new Cursor(Cursor.SE_RESIZE_CURSOR));
		eastSouthDecorationPane.setBackground(decorationColor);
		eastSouthDecorationPane.setPreferredSize(new Dimension(decorationThickness, decorationThickness * decorationCornerThicknessMultiplicator - decorationThickness));
		eastDecorationPane.add(BorderLayout.SOUTH, eastSouthDecorationPane);
	}

	private void setRootPane() {
		rootPane = new ExtendedPanel(1.0f);
		rootPane.setLayout(new BorderLayout());
		decorationPane.add(BorderLayout.CENTER, rootPane);

		componentMovementController.registerComponent(rootPane);
	}

	private void setContentPane() {
		contentPane = new ExtendedPanel(1.0f);
		contentPane.setLayout(DEFAULT_CONTENT_LAYOUT);
		rootPane.add(BorderLayout.CENTER, contentPane);
	}
}
